package cn.jbolt.config;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.setting.Setting;
import cn.jbolt.admin.auto.JBoltAutoInitData;
import cn.jbolt.admin.auto.JBoltAutoUpgrade;
import cn.jbolt.admin.monitor.systemlog.ProjectSystemLogProcessor;
import cn.jbolt.admin.user.extend.ExtendProjectOfModule;
import cn.jbolt.config.routes.*;
import cn.jbolt.core.Interceptor.JBoltOnlineUserGlobalInterceptor;
import cn.jbolt.core.api.JBoltApiInterceptor;
import cn.jbolt.core.api.log.JBoltApiLogInterceptor;
import cn.jbolt.core.base.config.JBoltConfig;
import cn.jbolt.core.base.config.JBoltProjectConfig;
import cn.jbolt.core.cache.JBoltOnlineUserCache;
import cn.jbolt.core.crontask.JBoltOnlineUserClearTask;
import cn.jbolt.core.enumutil.JBoltEnum;
import cn.jbolt.core.enumutil.JBoltEnumBean;
import cn.jbolt.core.handler.base.JBoltBaseHandler;
import cn.jbolt.core.handler.base.JBoltDruidStatViewHandler;
import cn.jbolt.core.kit.*;
import cn.jbolt.core.model.*;
import cn.jbolt.core.model.base.JBoltModelConfig;
import cn.jbolt.core.permission.JBoltUserAuthKit;
import cn.jbolt.core.plugin.JBoltActiveRecordPlugin;
import cn.jbolt.core.service.JBoltProjectSystemLogProcessor;
import cn.jbolt.core.util.JBoltCookieUtil;
import cn.jbolt.core.util.JBoltStringUtil;
import cn.jbolt.extend.permission.PermissionKey;
import cn.jbolt.platform.globalconfig.GlobalConfigApiController;
import com.jfinal.config.*;
import com.jfinal.ext.handler.UrlSkipHandler;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.cron4j.Cron4jPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.plugin.druid.IDruidStatViewAuth;
import com.jfinal.template.Engine;
import com.jfinal.upload.OreillyCos;
import com.oreilly.servlet.multipart.FileRenamePolicy;
import net.dreamlu.event.EventPlugin;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 此项目工程的配置类
 *
 * @ClassName: ProjectConfig
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2022年2月13日
 */
public class ProjectConfig extends JBoltProjectConfig {
    /**
     * 配置系统常量
     */
    @Override
    public void configConstants(Constants me) {
        // 二开项目常量配置
        ExtendProjectConfig.configConstant(me);
    }

    /**
     * 配置路由
     */
    @Override
    public void configRoutes(Routes me) {
        if(JBoltConfig.PLATFORM_INDEX_CONTROLLER_ENABLE){
            // index路由
            me.add(new JBoltPlatformIndexRoutes());
        }
        // 后台管理 JBolt内置模块接口路由
        me.add(new JBoltAdminRoutes());
        // 接口路由 - JBolt内置用户授权 权限 菜单 用户信息相关路由
        me.add(new JBoltUserAuthApiRoutes());
        // 验证码接口路由
        me.add(new JBoltLoginCaptchaApiRoutes());
        // 微信公众号接口路由
        me.add(new JBoltWechatAdminRoutes());
        me.add(new JBoltWechatApiRoutes());
        //开发文档 路由
        //me.add(new DevDocAdminRoutes()
        if(JBoltConfig.JBOLT_CODE_GEN_ENABLE){
            //代码生成专用理由
            me.add(new JBoltCodeGenRoutes());
        }
        //全局参数配置路由
        me.add(new JBoltGlobalConfigRoutes());
        //调用使用可视化生成器生成的路由配置信息
        ProjectCodeGenRoutesConfig.config(me);
        // 二开专用扩展路由配置
        ExtendProjectConfig.configRoute(me);
    }

    /**
     * 配置模板引擎
     */
    @Override
    public void configEngines(Engine me) {
        // 添加JBoltConfig的访问
        me.addSharedObject("JBoltConfig", new JBoltConfig());
        // 添加JBoltStringUtil的访问
        me.addSharedObject("JBoltStringUtil", new JBoltStringUtil());

        // 后台所有Dialog的页面 都是用这个layout
        me.addSharedFunction("/common/__admin_dialog_layout.html");
        // 后台所有Iframe的页面 都是用这个layout
        me.addSharedFunction("/common/__admin_iframe_layout.html");
        // 后台独立单页 都是用这个layout
        me.addSharedFunction("/common/__admin_singlepage_layout.html");
        // 后台所有JBoltLayer组件加载的页面 都是用这个layout
        me.addSharedFunction("/common/__admin_jboltlayer_layout.html");
        // 配置JBolt 自动识别Layout
        me.addSharedFunction("/common/__jbolt_layout.html");
        // 常用前端组件
        me.addSharedFunction("/common/__jboltassets.html");
        //可视化生成器 index.html专用 查询条件公用模板
        me.addSharedFunction("/codegen/config/_condition.html");
        //可视化生成器 _form.html专用 模板
        me.addSharedFunction("/codegen/config/_form_ele_define_function.html");

        // 二开扩展配置 模板引擎
        ExtendProjectConfig.configEngine(me);
    }

    /**
     * 配置系统插件
     */
    @Override
    public void configPlugins(Plugins me) {
        // 配置调度
        Cron4jPlugin cron4jPlugin = configCron4jPlugin(me);
        // 配置JFinal Event插件
        configJFinalEvent(me);

        //二开配置扩展插件
        ExtendProjectConfig.configPlugin(me, cron4jPlugin);
    }

    /**
     * 配置JFinal event
     *
     * @param me
     */
    private void configJFinalEvent(Plugins me) {
        // 初始化插件
        EventPlugin eventPlugin = new EventPlugin();
        // 设置为异步 或者线程池 二选一 异步默认单线程
        // eventPlugin.async();

        // 使用eventPlugin.threadPool(ExecutorService executorService)`自定义线程池。
        ExecutorService fixedThreadPool = new ThreadPoolExecutor(16, 32, 0L, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(1024));
        eventPlugin.threadPool(fixedThreadPool);
        // 执行扫描 多个package 使用分号;隔开 例如 pa;pb
        eventPlugin.enableClassScan().scanJar().scanPackage("cn.jbolt.event");
        me.add(eventPlugin);
    }

    /**
     * 配置自动调度插件
     *
     * @param me
     * @return
     */
    private Cron4jPlugin configCron4jPlugin(Plugins me) {
        Cron4jPlugin cron4jPlugin = new Cron4jPlugin();
        cron4jPlugin.addTask("0-59/1 * * * *", new JBoltOnlineUserClearTask());
        me.add(cron4jPlugin);
        return cron4jPlugin;
    }

    /**
     * 配置拦截器
     */
    @Override
    public void configInterceptors(Interceptors me) {
        //me.addGlobalActionInterceptor(new SessionInViewInterceptor());
        //判断是否开启配置全局API日志拦截器，配置后自行实现日志持久化
        if(JBoltConfig.PLATFORM_API_LOG_INTERCEPTOR_ENABLE){
            me.addGlobalActionInterceptor(new JBoltApiLogInterceptor());
        }
        //配置全局action拦截器 拦截判断用户是否被锁屏、解锁、强退、顶替
        me.addGlobalActionInterceptor(new JBoltOnlineUserGlobalInterceptor());
        //配置API拦截器 校验Http请求的Header格式参数设置和开放性OpenAPI
        me.addGlobalActionInterceptor(new JBoltApiInterceptor());
        //二开配置扩展全局拦截器
        ExtendProjectConfig.configInterceptor(me);
    }

    /**
     * 配置Sentinel限流
     */
    @Override
    protected void configSentinel() {
        //WebCallbackManager.setUrlBlockHandler(new JBoltSentinelUrlBlockHandler());
    }

    /**
     * 配置处理器handler
     */
    @Override
    protected void configHandlers(Handlers me, JBoltBaseHandler baseHandler) {
        //二开配置扩展全局处理器
        ExtendProjectConfig.configHandler(me, baseHandler);
    }

    /**
     * 配置
     */
    @Override
    protected void configWebsocketHandler(Handlers me) {
        me.add(new UrlSkipHandler("^/websocket.ws", false));
    }

    @Override
    protected void configDruidMonitor(Handlers me) {
        JBoltDruidStatViewHandler dvh = new JBoltDruidStatViewHandler("/admin/druid/monitor", new IDruidStatViewAuth() {
            public boolean isPermitted(HttpServletRequest request) {
                String token = request.getParameter("token");
                if(StrKit.isBlank(token)){
                    token = JBoltCookieUtil.getValue(request,"jboltdruidtoken");
                }
                if(StrKit.isBlank(token)){
                    return false;
                }
                // 从当前线程里拿到数据
                User user = JBoltOnlineUserCache.me.getUserBySessionId(token);
                if (user == null) {
                    return false;
                }
                if (!user.getEnable()) {
                    return false;
                }
                // 锁屏了不让看
                if (JBoltUserKit.userScreenIsLocked()) {
                    return false;
                }
                // 超级管理可以直接看
                if (user.getIsSystemAdmin()) {
                    return true;
                }
                return JBoltUserAuthKit.hasPermission(user.getId(), true, PermissionKey.DRUID_MONITOR);
            }
        });
        me.add(dvh);
    }

    @Override
    protected void configMainDbPlugins(DruidPlugin dbPlugin, JBoltActiveRecordPlugin arp, Engine sqlEngine) {
        //处理二开配置扩展
        ExtendProjectConfig.configMainDbPlugin(dbPlugin, arp, sqlEngine);
    }

    @Override
    protected void configExtendDbPlugins(DruidPlugin dbPlugin, JBoltActiveRecordPlugin arp, Engine sqlEngine, String configName, Setting dbSetting) {
        //处理二开配置扩展
        ExtendProjectConfig.configExtendDbPlugins(dbPlugin, arp, sqlEngine, configName, dbSetting);
    }

    /**
     * 配置cos组件
     */
    @Override
    protected void configCos() {
        OreillyCos.setFileRenamePolicy(new FileRenamePolicy() {
            @Override
            public File rename(File file) {
                String path = file.getPath();
                String ext = FileUtil.getSuffix(path);
                String name = FileUtil.getName(path);
                if (StrKit.isBlank(ext) && name.equals("blob")) {
                    ext = "jpg";
                }
                return new File(file.getParent(), IdUtil.fastSimpleUUID() + "." + ext);
            }
        });
    }

    @Override
    public void onStop() {
        //调用默认
        super.onStop();
        //二开扩展配置服务器关闭前处理
        ExtendProjectConfig.onStop();
    }

    @Override
    public void onStart() {
        //调用默认
        super.onStart();
        //0、自动初始化数据
        JBoltAutoInitData.me.exe();
        //1、启动后进行升级操作
        JBoltAutoUpgrade.me.exe();
        //2、配置action report输出位置
        JBoltConfig.configActionReportWriter();
        //3、配置JBoltAutoCacheLog
        JBoltConfig.configJBoltAutoCacheLog();
        //4、配置微信公众平台
        JBoltConfig.configWechat();
        //5、配置是否显示action reporter行号
        JBoltConfig.configJBoltActionReportLineNumberEnable();
        //6、加载enums和caches services等
        //loadCodeGenTranslateUseClasses();
        //二开扩展配置服务器启动后处理
        ExtendProjectConfig.onStart();
    }

    /**
     * 加载enums和caches services等
     */
    private void loadCodeGenTranslateUseClasses() {
        //new Thread(new Runnable() {
        //    @Override
        //    public void run() {
        //        JBoltCodeGenCache.me.getCodeGenEnums();
        //        JBoltCodeGenCache.me.getCodeGenCaches();
        //        JBoltCodeGenCache.me.getCodeGenServices();
        //    }
        //}).start();
    }

    @Override
    protected void configSaas(Handlers me, JBoltBaseHandler baseHandler) {

    }

    /**
     * 配置租户SN转ID的转换器
     * @return
     */
    @Override
    protected JBoltSaasTenantSnToIdProcessor getSaasTenantSnToIdProcessor() {
        return ExtendProjectConfig.configSaasTenantSnToIdProcessor();
    }

    /**
     * 配置租户sn转name的转换器
     * @return
     */
    @Override
    protected JBoltSaasTenantSnToNameProcessor getSaasTenantSnToNameProcessor() {
        return ExtendProjectConfig.configSaasTenantSnToNameProcessor();
    }

    /**
     * 配置租户ID转Name的转换器
     * @return
     */
    @Override
    protected JBoltSaasTenantIdToNameProcessor getSaasTenantIdToNameProcessor() {
        return ExtendProjectConfig.configSaasTenantIdToNameProcessor();
    }

    /**
     * 配置租户ID转sn的转换器
     * @return
     */
    @Override
    protected JBoltSaasTenantIdToSnProcessor getSaasTenantIdToSnProcessor() {
        return ExtendProjectConfig.configSaasTenantIdToSnProcessor();
    }

    /**
     * 配置租户获得当前是否可访问的处理器
     * @return
     */
    @Override
    protected JBoltSaasTenantAccessibleProcessor getSaasTenantAccessibleProcessor() {
        return ExtendProjectConfig.configSaasTenantAccessibleProcessor();
    }

    /**
     * 配置租户列表获取的处理器
     * @return
     */
    @Override
    protected JBoltSaasTenantGetAllProcessor getSaasTenantAllDatasProcessor() {
        return ExtendProjectConfig.configSaasTenantAllDatasProcessor();
    }

    @Override
    protected List<JBoltEnumBean> getExtendOfModules() {
        return JBoltEnum.getEnumOptionList(ExtendProjectOfModule.class);
    }

    @Override
    protected String getExtendOfModuleEnumClassSimpleName() {
        return ExtendProjectOfModule.class.getSimpleName();
    }

    /**
     * 手动管理添加 新开项目的需要给租户分表的Model
     */
    @SuppressWarnings("unchecked")
    @Override
    protected void configProjectSaasSeparateTableModels() {
        //项目里不包含在jbolt_core中的需要分表的加入model
        JBoltModelConfig.me.addProjectSeparateModels(
                SysNotice.class,
                UserExtend.class,
                SysNoticeReader.class,
                Todo.class,
                PrivateMessage.class
        );
        //二开业务专用的配置调用
        ExtendProjectConfig.configProjectSaasSeparateTableModels();
    }

    @Override
    protected JBoltProjectSystemLogProcessor getProjectSystemLogProcessor() {
        return new ProjectSystemLogProcessor();
    }
}
